package org.maven.dependencybrowser.assembler;
/**********************************************************************
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

Contributors:

**********************************************************************/
import org.apache.maven.artifact.Artifact;
import org.apache.maven.artifact.factory.ArtifactFactory;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import org.apache.maven.model.Dependency;
import org.apache.maven.model.Model;
import org.apache.maven.model.Parent;
import org.apache.maven.project.ModelUtils;
import org.apache.maven.project.inheritance.ModelInheritanceAssembler;
import org.apache.maven.project.interpolation.ModelInterpolationException;
import org.apache.maven.project.interpolation.ModelInterpolator;
import org.maven.dependencybrowser.implementations.Repository;

import java.util.*;

/**
 * Provides pom inheritance & properties support
 */
public class Assembler
{
    public static final String ROLE = "org.maven.dependencybrowser.assembler.Assembler";

    private ModelInheritanceAssembler assembler;
    private ModelInterpolator interpolator;
    private ArtifactFactory artifactFactory;

    /**
     * create a list of pom parents starting with the raw pom
     */
    private void lookupLineage(Model child, Repository repository, List lineage)
    {
        if (child == null)
        {
            return;
        }
        lineage.add(child);
        Parent parentStub = child.getParent();
        if (parentStub == null)
        {
            return;
        }
        Model parent = repository.resolveFile(parentStub.getGroupId(), parentStub.getArtifactId(), parentStub.getVersion());
        lookupLineage(parent, repository, lineage);
    }

    /**
     * work down the pom parents, merging the the parent into the child
     */
    private Model assemble(Model parent, List lineage, Repository repository)
    {
        if (lineage.size() == 0)
        {
            return parent;
        }
        Model child = (Model) lineage.remove(0);
        assembler.assembleModelInheritance(child, parent);
        return assemble(child, lineage, repository);
    }

    private Model dependencyManagement(Model interpolated) throws AssemblerException
    {
        try
        {
            if (interpolated.getDependencyManagement() == null)
            {
                return interpolated;
            }
            Model managed = ModelUtils.cloneModel(interpolated);
            Map managees = new HashMap();
            for (Iterator iter = managed.getDependencies().iterator(); iter.hasNext();)
            {
                Dependency dependency = (Dependency) iter.next();
                managees.put(dependency.getGroupId() + ":" + dependency.getArtifactId(), dependency);
            }
            for (Iterator iter = interpolated.getDependencyManagement().getDependencies().iterator(); iter.hasNext();)
            {
                Dependency manager = (Dependency) iter.next();
                VersionRange versionRange = VersionRange.createFromVersionSpec(manager.getVersion());
                Artifact artifact = artifactFactory.createDependencyArtifact(manager.getGroupId(), manager.getArtifactId(), versionRange, manager.getType(), manager.getClassifier(), manager.getScope(), manager.isOptional());
                Dependency managee = (Dependency) managees.get(manager.getGroupId() + ":" + manager.getArtifactId());
                if (managee != null)
                {
                    managee.setVersion(artifact.getVersion());
                }
            }
            return managed;
        }
        catch (InvalidVersionSpecificationException e)
        {
            throw new AssemblerException(e);
        }
    }

    public AssembledModel assemble(Model raw, Repository repository) throws AssemblerException
    {
        try
        {
            List lineage = new ArrayList();
            lookupLineage(raw, repository, lineage);
            Collections.reverse(lineage);
            Model root = (Model) lineage.remove(0);
            Model assembled = assemble(root, lineage, repository);
            Model interpolated = interpolator.interpolate(assembled, new HashMap());
            Model managed = dependencyManagement(interpolated);
            return new AssembledModel(managed);
        }
        catch (ModelInterpolationException e)
        {
            throw new AssemblerException(e);
        }
    }
}
